/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.service.evaluation;

import cn.ac.nercis.pes.common.utils.DataUtils;
import cn.ac.nercis.pes.common.utils.JsonUtils;
import cn.ac.nercis.pes.common.constant.evaluation.ContentNode;
import cn.ac.nercis.pes.model.dto.common.Result;
import cn.ac.nercis.pes.model.dto.evaluation.content.*;
import cn.ac.nercis.pes.repository.dal.evaluation.EvaluationContentRepository;
import cn.ac.nercis.pes.repository.dal.manage.ConstantRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemRepository;
import cn.ac.nercis.pes.repository.model.evaluation.EvaluationContentDO;
import cn.ac.nercis.pes.repository.model.project.SystemDO;
import cn.ac.nercis.pes.repository.utils.DbUtils;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * 测评内容信息服务类
 *
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public record EvaluationContentService(EvaluationContentRepository evaluationContentRepository,
                                       SystemRepository systemRepository,
                                       ConstantRepository constantRepository) {
    private final static String BASIS_STANDARD_CONSTANT = "依据标准和规范_默认数据";
    private final static String REF_STANDARD_CONSTANT = "参考标准和规范_默认数据";

    @Autowired
    public EvaluationContentService {
    }

    /**
     * 修复重复数据
     */
    public void repairDuplicateData(@NonNull String systemId, @NonNull ContentNode node){
        var source = this.evaluationContentRepository.findBySystemIdAndNode(systemId, node);
        var duplicateData = DbUtils.findDuplicateData(source);
        if(CollectionUtils.isNotEmpty(duplicateData)){
            this.evaluationContentRepository.deleteAll(duplicateData);
        }
    }

    public EvaluationBasisDTO findEvaluationBasis(String systemId){
        this.repairDuplicateData(systemId, ContentNode.EVALUATION_BASIS);
        return DataUtils.findFirst(this.evaluationContentRepository.findBySystemIdAndNode(systemId, ContentNode.EVALUATION_BASIS))
                .map(data -> JsonUtils.fromJson(data.getData(), EvaluationBasisDTO.class))
                .map(eb->{
                    if(CollectionUtils.isEmpty(eb.getBasisStandard())){
                        eb.setBasisStandard(defaultBasisStandards());
                    }
                    if(CollectionUtils.isEmpty(eb.getRefStandard())){
                        eb.setRefStandard(defaultRefStandards());
                    }
                    return eb;
                })
                .orElse(EvaluationBasisDTO.builder()
                        .basisStandard(defaultBasisStandards())
                        .refStandard(defaultRefStandards())
                        .build());
    }

    public synchronized Result<String> saveEvaluationBasis(String systemId, EvaluationBasisDTO data) {
        return saveEvaluationContent(systemId, ContentNode.EVALUATION_BASIS,
                JsonUtils.toJson(data));
    }

    private List<EvaluationBasisOptionDTO> defaultBasisStandards(){
        return constantRepository.findAllByNameOrderBySort(BASIS_STANDARD_CONSTANT)
                .stream()
                .map(c->EvaluationBasisOptionDTO.builder()
                        .selected(true)
                        .name(c.getValue())
                        .build())
                .toList();
    }

    private List<EvaluationBasisOptionDTO> defaultRefStandards(){
        return constantRepository.findAllByNameOrderBySort(REF_STANDARD_CONSTANT)
                .stream()
                .map(c->EvaluationBasisOptionDTO.builder()
                        .selected(true)
                        .name(c.getValue())
                        .build())
                .toList();
    }

    public BusinessConditionDTO findBusinessCondition(String systemId) {
        this.repairDuplicateData(systemId, ContentNode.BUSINESS_CONDITION);
        var systemDesc = this.systemRepository.findById(systemId)
                .map(SystemDO::getDesc)
                .orElse("");
        return DataUtils.findFirst(this.evaluationContentRepository.findBySystemIdAndNode(systemId, ContentNode.BUSINESS_CONDITION))
                .map(data -> {
                    BusinessConditionDTO condition = JsonUtils.fromJson(data.getData(), BusinessConditionDTO.class);
                    if (Objects.isNull(condition)) {
                        condition = BusinessConditionDTO.builder()
                                .content(data.getData())
                                .build();
                    }
                    return condition;
                })
                .orElse(BusinessConditionDTO
                        .builder()
                        .content(systemDesc)
                        .build());
    }

    public synchronized Result<String> saveBusinessCondition(String systemId, BusinessConditionDTO data) {
        return saveEvaluationContent(systemId, ContentNode.BUSINESS_CONDITION,
                JsonUtils.toJson(data));
    }

    public NetworkTopologyDTO findNetworkTopology(String systemId) {
        this.repairDuplicateData(systemId, ContentNode.NETWORK_TOPOLOGY);
        return DataUtils.findFirst(this.evaluationContentRepository.findBySystemIdAndNode(systemId, ContentNode.NETWORK_TOPOLOGY))
                .map(data -> JsonUtils.fromJson(data.getData(), NetworkTopologyDTO.class))
                .orElse(NetworkTopologyDTO.builder().build());
    }

    public synchronized Result<String> saveNetworkTopology(String systemId, NetworkTopologyDTO data) {
        return saveEvaluationContent(systemId, ContentNode.NETWORK_TOPOLOGY,
                JsonUtils.toJson(data));
    }

    public PasswordConditionDTO findPasswordCondition(String systemId) {
        this.repairDuplicateData(systemId, ContentNode.PASSWORD_CONDITION);
        return DataUtils.findFirst(this.evaluationContentRepository.findBySystemIdAndNode(systemId, ContentNode.PASSWORD_CONDITION))
                .map(data -> JsonUtils.fromJson(data.getData(), PasswordConditionDTO.class))
                .orElse(PasswordConditionDTO.builder().build());
    }

    public synchronized Result<String> savePasswordCondition(String systemId, PasswordConditionDTO data) {
        return saveEvaluationContent(systemId, ContentNode.PASSWORD_CONDITION,
                JsonUtils.toJson(data));
    }

    public ToolCheckPointDTO findToolCheckPoint(String systemId) {
        this.repairDuplicateData(systemId, ContentNode.TOOL_CHECK_POINT);
        return DataUtils.findFirst(this.evaluationContentRepository.findBySystemIdAndNode(systemId, ContentNode.TOOL_CHECK_POINT))
                .map(data -> JsonUtils.fromJson(data.getData(), ToolCheckPointDTO.class))
                .orElse(ToolCheckPointDTO.builder().build());
    }

    public synchronized Result<String> saveToolCheckPoint(String systemId, ToolCheckPointDTO data) {
        return saveEvaluationContent(systemId, ContentNode.TOOL_CHECK_POINT,
                JsonUtils.toJson(data));
    }

    public LastEvaluationDTO findLastEvaluation(String systemId) {
        this.repairDuplicateData(systemId, ContentNode.LAST_EVALUATION);
        return DataUtils.findFirst(this.evaluationContentRepository.findBySystemIdAndNode(systemId, ContentNode.LAST_EVALUATION))
                .map(data -> JsonUtils.fromJson(data.getData(), LastEvaluationDTO.class))
                .orElse(LastEvaluationDTO.builder().build());
    }

    public synchronized Result<String> saveLastEvaluation(String systemId, LastEvaluationDTO data) {
        return saveEvaluationContent(systemId, ContentNode.LAST_EVALUATION,
                JsonUtils.toJson(data));
    }

    public synchronized Result<String> saveEvaluationContent(String systemId, ContentNode node, String data) {
        EvaluationContentDO evaluationContentDO = getEvaluationContentAndRemoveRepeat(systemId, node);
        if (Objects.isNull(evaluationContentDO)) {
            evaluationContentDO = EvaluationContentDO.builder()
                    .systemId(systemId)
                    .node(node)
                    .data(data)
                    .build();
            evaluationContentDO.setCreateTime(LocalDateTime.now());
        } else {
            evaluationContentDO.setNode(node);
            evaluationContentDO.setData(data);
            evaluationContentDO.setModifyTime(LocalDateTime.now());
        }
        return Optional.of(evaluationContentDO)
                .map(this.evaluationContentRepository::save)
                .map(EvaluationContentDO::getId)
                .map(Result::success)
                .orElse(Result.failed());
    }

    public EvaluationContentDO getEvaluationContentAndRemoveRepeat(String systemId, ContentNode node){
        var data = this.evaluationContentRepository.findBySystemIdAndNode(systemId, node);
        var firstData = data.stream().findFirst().orElse(null);
        if(Objects.nonNull(firstData) && data.size()>DataUtils.SINGLE_COUNT){
            var repeatData = data.stream()
                    .filter(r->!StringUtils.equals(r.getId(),firstData.getId()))
                    .toList();
            this.evaluationContentRepository.deleteAll(repeatData);
        }
        return firstData;
    }

}